﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace NumberPuzzle
{
    public partial class BoardNumber : UserControl
    {
        #region Declarations

        private Button[,] piece;
        private int rowEmpty;
        private int colEmpty;

        /// <summary>
        /// Actual puzzle numbers.
        /// </summary>
        public int[,] puzzle;

        public BoardNumber()
        {
            InitializeComponent();
            piece = new Button[3, 3];
            puzzle = new int[piece.GetLength(0), piece.GetLength(1)];
            int number = 1;
            int sizeX = this.Width / piece.GetLength(0);
            int sizeY = this.Height / piece.GetLength(1);
            for (int i = 0; i < piece.GetLength(0); i++)
            {
                for (int j = 0; j < piece.GetLength(1); j++)
                {
                    piece[i, j] = new Button();
                    piece[i, j].Text = number.ToString();
                    piece[i, j].Size = new Size(sizeX, sizeY);
                    piece[i, j].Location = new Point(j * sizeX, i * sizeY);
                    piece[i, j].Tag = new Point(i, j);
                    piece[i, j].Font = new Font("Microsoft Sans Serif", 20F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
                    piece[i, j].Click += new EventHandler(BoardNumber_Click);
                    if (number % 2 == 0)
                    {
                        piece[i, j].ForeColor = Color.Blue;
                        piece[i, j].BackColor = Color.White;
                    }
                    else
                    {
                        piece[i, j].ForeColor = Color.White;
                        piece[i, j].BackColor = Color.Brown;
                    }
                    piece[i, j].Visible = number != piece.GetLength(0) * piece.GetLength(1);
                    this.Controls.Add(piece[i, j]);
                    puzzle[i, j] = number;
                    number++;
                }
            }
            rowEmpty = piece.GetLength(0) - 1;
            colEmpty = piece.GetLength(1) - 1;
        }

        #endregion Declarations

        #region Private Methods

        private void BoardNumber_Click(object sender, EventArgs e)
        {
            int row = ((Point)((Button)sender).Tag).X;
            int col = ((Point)((Button)sender).Tag).Y;
            MovePiece(row, col, true);
        }

        private bool ExistsPiece(int row, int col)
        {
            return (row >= 0 && row < piece.GetLength(0) && col >= 0 && col < piece.GetLength(1));
        }

        private bool CanMovePiece(int row, int col)
        {
            int width = piece[0, 0].Width;
            int height = piece[0, 0].Height;
            return piece[rowEmpty, colEmpty].Location.X - piece[row, col].Location.X == 0 &&
                   piece[rowEmpty, colEmpty].Location.Y - piece[row, col].Location.Y == height
                || piece[rowEmpty, colEmpty].Location.X - piece[row, col].Location.X == width &&
                   piece[rowEmpty, colEmpty].Location.Y - piece[row, col].Location.Y == 0
                || piece[row, col].Location.X - piece[rowEmpty, colEmpty].Location.X == 0 &&
                   piece[row, col].Location.Y - piece[rowEmpty, colEmpty].Location.Y == height
                || piece[row, col].Location.X - piece[rowEmpty, colEmpty].Location.X == width &&
                   piece[row, col].Location.Y - piece[rowEmpty, colEmpty].Location.Y == 0;

        }

        private bool Resolved()
        {
            int lastNumber = puzzle[0, 0];

            for (int i = 0; i < piece.GetLength(0); i++)
            {
                for (int j = 0; j < piece.GetLength(1); j++)
                {
                    if (lastNumber > puzzle[i, j])
                    {
                        return false;
                    }

                    lastNumber = puzzle[i, j];
                }
            }
            return true;
        }

        #endregion Private Methods

        #region Public Methods

        /// <summary>
        /// Moves the a piece of the puzzle (draws the change).
        /// </summary>
        /// <param name="row">Puzzle´s row number.</param>
        /// <param name="col">Puzzle´s column number.</param>
        public void MovePiece(int row, int col, bool checkSolved)
        {
            if (ExistsPiece(row, col) && CanMovePiece(row, col))
            {
                piece[rowEmpty, colEmpty].Visible = true;
                piece[row, col].Visible = false;

                string sAux = piece[rowEmpty, colEmpty].Text;
                int numberAux = puzzle[rowEmpty, colEmpty];

                piece[rowEmpty, colEmpty].Text = piece[row, col].Text;
                puzzle[rowEmpty, colEmpty] = puzzle[row, col];

                piece[row, col].Text = sAux;
                puzzle[row, col] = numberAux;

                Color colAux = piece[rowEmpty, colEmpty].BackColor;
                piece[rowEmpty, colEmpty].BackColor = piece[row, col].BackColor;
                piece[row, col].BackColor = colAux;

                colAux = piece[rowEmpty, colEmpty].ForeColor;
                piece[rowEmpty, colEmpty].ForeColor = piece[row, col].ForeColor;
                piece[row, col].ForeColor = colAux;

                rowEmpty = row;
                colEmpty = col;

                if (Resolved() && checkSolved)
                {
                    MessageBox.Show("Puzzle Resuelto", "Game", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }

        /// <summary>
        /// Initializes the puzzle with random numbers from 1 to 8.
        /// </summary>
        public void Initialize(int[,] puzzle)
        {
            for (int i = 0; i < piece.GetLength(0); i++)
            {
                for (int j = 0; j < piece.GetLength(1); j++)
                {
                    this.puzzle[i, j] = puzzle[i, j];
                    piece[i, j].Text = puzzle[i, j].ToString();
                    if (puzzle[i, j] % 2 == 0)
                    {
                        piece[i, j].ForeColor = Color.Blue;
                        piece[i, j].BackColor = Color.White;
                    }
                    else
                    {
                        piece[i, j].ForeColor = Color.White;
                        piece[i, j].BackColor = Color.Brown;
                    }
                    piece[i, j].Visible = puzzle[i, j] != piece.GetLength(0) * piece.GetLength(1);
                }
            }
            rowEmpty = piece.GetLength(0) - 1;
            colEmpty = piece.GetLength(1) - 1;
        }

        /// <summary>
        /// Disorder the pieces of the puzzle.
        /// </summary>
        /// <param name="movements">The number of movements.</param>
        public void DisOrderPuzzle(int movements)
        {
            Random r = new Random();

            while (movements > 0)
            {
                bool found = false;
                int row=0, column = 0;

                for (int i = 0; i < piece.GetLength(0); i++)
                {
                    for (int j = 0; j < piece.GetLength(1); j++)
                    {
                        if (puzzle[i, j] == piece.GetLength(0) * piece.GetLength(1))
                        {
                            found = true;
                            row = i;
                            column = j;
                            break;
                        }
                    }

                    if (found)
                    {
                        break;
                    }
                }

                if ((row == 0 || row == 2) && (column == 0 || column == 2))
                {
                    // 2 pieces available to de moved.
                    if (r.Next(0, 2) == 0)
                    {
                        row = Math.Abs(row-1);
                    }
                    else
                    {
                        column = Math.Abs(column-1);
                    }
                }
                else if ((row == 1) && (column == 1))
                {
                    int number = r.Next(0, 4);
                    switch (number)
                    {
                        case 0:
                            row = row - 1;
                            break;

                        case 1:
                            row++;
                            break;

                        case 2:
                            column = column - 1;
                            break;

                        case 3:
                            column++;
                            break;
                    }
                }
                else
                {
                    // 3 pieces available to de moved.
                    int number = r.Next(0, 3);
                    switch (number)
                    {
                        case 0:
                            row = Math.Abs(row - 1);
                            break;
                        case 1:
                            column = Math.Abs(column - 1);
                            break;
                        case 2:
                            column = Math.Abs(column + 1);
                            break;
                    }
                }

                this.MovePiece(row, column, false);
                movements--;

                this.Refresh();
                System.Threading.Thread.Sleep(50);
            }
        }

        #endregion Private Methods
    }
}
